<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="wqVz5" id="wqVz5"><span data-lake-id="u8207869b" id="u8207869b">典型回答</span></h1>
  <p data-lake-id="uc68094fb" id="uc68094fb"><span data-lake-id="u2d3ef12f" id="u2d3ef12f" class="lake-fontsize-12">在网络编程中，基本都是基于TCP报文的字节流的操作，所以Java的NIO又新增了ByteBuffer，只不过Java原生的ByteBuffer，非常难操作，也不能扩缩容，所以Netty又重新封装了自己的Bytebuf，除了性能上的优势之外，Netty的Buffer在使用上相对于NIO也非常简洁，有如下特点：</span></p>
  <h2 data-lake-id="JAhpe" id="JAhpe"><span data-lake-id="u9f4cb3df" id="u9f4cb3df">动态扩缩容</span></h2>
  <p data-lake-id="u42df6010" id="u42df6010"><span data-lake-id="u14f28263" id="u14f28263" class="lake-fontsize-12">顾名思义，Netty中的ByteBuffer可以像Java中的ArrayList一样，根据写入数据的字节数量，自动扩容。代码如下所示：</span></p>
  <pre lang="java"><code>
final void ensureWritable0(int minWritableBytes) {
    final int writerIndex = writerIndex();
    final int targetCapacity = writerIndex + minWritableBytes;
    // using non-short-circuit &amp; to reduce branching - this is a hot path and targetCapacity should rarely overflow
    if (targetCapacity &gt;= 0 &amp; targetCapacity &lt;= capacity()) {
        ensureAccessible();
        return;
    }
    if (checkBounds &amp;&amp; (targetCapacity &lt; 0 || targetCapacity &gt; maxCapacity)) {
        ensureAccessible();
        throw new IndexOutOfBoundsException(String.format(
                "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
                writerIndex, minWritableBytes, maxCapacity, this));
    }

    // Normalize the target capacity to the power of 2.
    final int fastWritable = maxFastWritableBytes();
    int newCapacity = fastWritable &gt;= minWritableBytes ? writerIndex + fastWritable
            : alloc().calculateNewCapacity(targetCapacity, maxCapacity);

    // Adjust to the new capacity. 【此处进行扩容】
    capacity(newCapacity);
}
</code></pre>
  <p data-lake-id="ue19b5871" id="ue19b5871"><span data-lake-id="u329f3bd2" id="u329f3bd2" class="lake-fontsize-12">这个在编写代码的时候，满足ByteBuf最大缓冲区的情况下，我们可以毫无顾忌地调用#write方法增加字节，而不用手动去check容量满足，然后去重新申请</span></p>
  <h2 data-lake-id="xssj9" id="xssj9"><span data-lake-id="u0e1df119" id="u0e1df119">读写指针代替#flip</span></h2>
  <h3 data-lake-id="BdKJH" id="BdKJH"><span data-lake-id="u2e79d188" id="u2e79d188">原生ByteBuffer的弊端</span></h3>
  <p data-lake-id="u39ee88c7" id="u39ee88c7"><span data-lake-id="u339354bc" id="u339354bc" class="lake-fontsize-12">Java原生的ByteBuffer的数据结构，分为limit，capacity两个指针，如果我们写入“Hollis”之后，ByteBuffer的内容如下：</span></p>
  <p data-lake-id="u1e8e4651" id="u1e8e4651"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673180746706-927a77a2-0cda-44b9-b2f9-648b7e172063.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u0e22e625" id="u0e22e625"><span data-lake-id="u964be6c3" id="u964be6c3">此时，如果我们要从该ByteBuffer中read数据，ByteBuffer会默认从position开始读，这样就什么也读不到，所以我们必须调用#flip方法，将position指针移动，如下：</span></p>
  <p data-lake-id="u9a90c95b" id="u9a90c95b"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673180814196-2d41af9c-8bed-4ad8-93e4-d783655875db.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u33ae7773" id="u33ae7773"><span data-lake-id="u43c6e9fe" id="u43c6e9fe">这样我们才可以读到“Hollis”这个数据，万一我们调用的时候忘记使用flip，就会很坑爹。</span></p>
  <h3 data-lake-id="DnxPc" id="DnxPc"><span data-lake-id="u1c23e727" id="u1c23e727">Netty的ByteBuf</span></h3>
  <p data-lake-id="u7fb00d16" id="u7fb00d16"><span data-lake-id="uc26814cd" id="uc26814cd">Netty自带的ByteBuf通过读写双指针避免了上面的问题，假如我们写入“Hollis”后，ByteBuf的内容如下：</span></p>
  <p data-lake-id="u84dae83b" id="u84dae83b"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673181168872-9e2ca4ff-d145-4ad4-83f0-0f3cffce1503.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc4a5a7c3" id="uc4a5a7c3"><span data-lake-id="ub0c76fb6" id="ub0c76fb6">在写入的同时，我们可以直接通过readPointer读取数据，如下所示：</span></p>
  <p data-lake-id="u84f9caad" id="u84f9caad"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673181214917-e8c4598e-3fc0-48eb-a0f7-ce7bc8bef99c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_18%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc137635f" id="uc137635f"><span data-lake-id="u42891264" id="u42891264">在这个过程中，我们完全不用像JavaNIO的ByteBufer一样，感知其结构内部的操作，也不用调用flip，随意的读取和写入即可。</span></p>
  <p data-lake-id="ub90a2351" id="ub90a2351"><span data-lake-id="u8950eb4e" id="u8950eb4e">同时，假如我们读Hollis这个数据，读到了一半，还剩下“is”没有读完，我们可以调用discardReadBytes方法将指针移位，为可写区域增加空间，如下所示：</span></p>
  <p data-lake-id="uf61d8611" id="uf61d8611"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1673181482582-f07018c7-1091-4e78-ae0d-ec336660e921.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_18%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h3 data-lake-id="JoRKe" id="JoRKe"><span data-lake-id="u764300cc" id="u764300cc">多种ByteBuf实现</span></h3>
  <p data-lake-id="ub298d322" id="ub298d322"><span data-lake-id="u5742223b" id="u5742223b">Netty根据不同的场景，有不同的ByteBuf实现，主要的几种分别是：Pooled，UnPooled，Direct，Heap，列表格如下：</span></p>
  <table data-lake-id="DfMob" id="DfMob" margin="true" width-mode="contain" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="250">
    <col width="250">
    <col width="250">
   </colgroup>
   <tbody>
    <tr data-lake-id="u605ce01b" id="u605ce01b">
     <td data-lake-id="u8af4d0a2" id="u8af4d0a2"></td>
     <td data-lake-id="u6bb46359" id="u6bb46359">
      <p data-lake-id="u8d0beda2" id="u8d0beda2"><span data-lake-id="u2d2d798c" id="u2d2d798c">Pooled</span></p></td>
     <td data-lake-id="u292b7ac7" id="u292b7ac7">
      <p data-lake-id="u4b7371f0" id="u4b7371f0"><span data-lake-id="u3556d036" id="u3556d036">UnPooled</span></p></td>
    </tr>
    <tr data-lake-id="u37aceb82" id="u37aceb82">
     <td data-lake-id="u5363b767" id="u5363b767">
      <p data-lake-id="u57bcd75f" id="u57bcd75f"><span data-lake-id="ua71e7053" id="ua71e7053">HeapByteBuf</span></p></td>
     <td data-lake-id="u5827eed5" id="u5827eed5">
      <p data-lake-id="uf5c944e8" id="uf5c944e8"><span data-lake-id="ue57d4c03" id="ue57d4c03">业务处理使用+高并发</span></p></td>
     <td data-lake-id="u6f59edaa" id="u6f59edaa">
      <p data-lake-id="u851e7503" id="u851e7503"><span data-lake-id="u1ac000bd" id="u1ac000bd">业务处理使用+正常流量</span></p></td>
    </tr>
    <tr data-lake-id="u10287e6d" id="u10287e6d">
     <td data-lake-id="u3f930639" id="u3f930639">
      <p data-lake-id="u35e3567e" id="u35e3567e"><span data-lake-id="ucc334e3c" id="ucc334e3c">DireactByteBuf</span></p></td>
     <td data-lake-id="uce0e3ac4" id="uce0e3ac4">
      <p data-lake-id="u8eba19b3" id="u8eba19b3"><span data-lake-id="ucea504fc" id="ucea504fc">Socket相关操作使用+高并发</span></p></td>
     <td data-lake-id="u61df5732" id="u61df5732">
      <p data-lake-id="u636c2a56" id="u636c2a56"><span data-lake-id="u59162975" id="u59162975">Socket相关操作使用+正常流量</span></p></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u461900fd" id="u461900fd"><span data-lake-id="u1c7dd7b3" id="u1c7dd7b3" class="lake-fontsize-12">当然Netty中的Buffer性能相比于Java NIO的Buffer也更强，譬如我们熟知的Zero-Copy等，这个我们放到另一篇中剖析</span></p>
 </body>
</html>